#include <string.h>

#include "lib.h"

#include "cdrawhand.h"
#include "cardmgr.h"
#include "csranks.h"
#include "shue.h"


//*****************************************************************************************************
//
// This class handles the logic of ranking Caribbean Draw hands, as well as deciding which cards should
// be discarded and replaced with new cards.  The basic ranking logic is the same as is used by
// caribbean stud, thus we derive from CStudHand.  The CDraw additions are the discard and draw logic.
//
// kharmon.  09-20-07.  #16842.
//*****************************************************************************************************


//////////////////////////////////////////////////////////////////////////////////////////////////////////
// constructor
//////////////////////////////////////////////////////////////////////////////////////////////////////////
CDrawHand::CDrawHand(CardMgr* Mgr)
	: CStudHand(Mgr)
{

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// return true if the dealer has at least a pair of eights.
//////////////////////////////////////////////////////////////////////////////////////////////////////////
bool8 
CDrawHand::qualifies()
{
    bool bRetVal = false;
    
    if(handRank > HR_PAIR)
    {
        // everything greater than a single pair automatically qualifies
        bRetVal = true;
    }
    else if(handRank > HR_HIGHCARD)
    {
        // so we have a pair...see if it's 8's or better.
        int nRank = ((uint8 *)memchr(counts,2,13)) - counts;

        nRank += 2;     // rank is always 2 more than array offset since rank of "2" is at array offset 0.

        if(nRank>=8)
        {
            // yep, we qualify.
            bRetVal=true;
        }
    }

    return bRetVal;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Called to figure out which cards to discard and replace in the dealer's hand, according to the cdraw
// rules.
//
//  pShue     -  (in, out) The deck that we will deal any new replacement cards from.
//  pNewCards -  (out)     Put a copy of any drawn cards in here, we need to know about them later.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CDrawHand::drawDealerCards(Shue *pShue, Hand *pNewCards)
{
    // what rank was is this hand now, before drawing?
    switch(getRank())
    {
        case HR_HIGHCARD:
        {
            // starting with just a high card.  Replace the two lowest cards.
            processHighCard(pShue, pNewCards);
            break;
        }

        case HR_2PAIR:
        {
            // starting with two pair, replace the non-pair card.
            processTwoPair(pShue, pNewCards);
            break;
        }

        case HR_3OFAKIND:
        {   
            // starting with 3 of a kind, replace the two non-set cards.
            processThreeOfAKind(pShue, pNewCards);
            break;
        }

        case HR_PAIR:
        {
            // starting with a pair, replace the two lowest non-pair cards.
            processPair(pShue, pNewCards);
            break;
        }
    }

    rankHand();
    calcNthHighest(0);
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Called to figure out which cards to discard and replace in the dealer's hand, according to the cdraw
// rules, when the dealer only has a high card.
//
//  pShue     -  (in, out) The deck that we will deal any new replacement cards from.
//  pNewCards -  (out)     Put a copy of any drawn cards in here, we need to know about them later.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CDrawHand::processHighCard(Shue *pShue, Hand *pNewCards)
{
    // *****------ first check for a flush
    uint8 abySuits[4];

    memset(abySuits, 0, sizeof(abySuits));

    for(int32 nI=0;nI<5;++nI)
    {
        // count cards of each suit
        abySuits[(mgr->getSuit(getCardAt(nI))>>4) - 1]++;
    }

    for(nI=0;nI<4;++nI)
    {
        // look for a suit that we have 4 cards of.
        if(abySuits[nI]==4)
        {
            break;
        }
    }

    if(nI!=4)
    {
        // we have a near flush, so draw 1 card to try and get hit the flush.
        uint8 ucSuit = ((uint8) (nI+1)<<4);

        // find the card with the non-flush suit and replace it with a fresh card.
        for(nI=0;nI<5;++nI)
        {
            if(mgr->getSuit(getCardAt(nI))!=ucSuit)
            {
                uint8 ucNewCard = pShue->getCard();
                pNewCards->add(ucNewCard);
                getCards()[nI] = ucNewCard;
                break;
            }
        }

        // we've drawn our card, return.
        return;
    }


    // *****------ okay, there was no near flush.  SO, now look for a missed outside straight.
    uint8 aucCounts[13];
	memset(aucCounts, 0, sizeof(aucCounts));

    // mark the rank of the cards we have in the aucCounts array.
	for (nI = 0 ; nI < 5; nI++)
	{
		uint8 ucRank = mgr->getRank(getCardAt(nI));

		if (ucRank >= 2)
		{
			++aucCounts[ucRank - 2];
		}
	}

    // now, examine the rank "aucCounts" array to see if we have any outside straight draws
    bool8 bAceLow=false;
    int32 nHits=0;
    int32 nTopRank=0;
    for(nI=0;nI<13;++nI)
    {
        if(aucCounts[nI]>0)
        {
            ++nHits;

            if(nHits==4)
            {
                // we've found our 4 in a row.  We'll draw one extra card.
                nTopRank=nI;
                break;
            }
            else if(nHits==3 && nI==2 && aucCounts[12]==1 && aucCounts[4]==0)
            {
                // if we have a 2-3-4, and an Ace, then we have a chance for an Ace-low straight. 
                // BUT, if we have a 2-3-4 and a 6, and an ACE...go for the inside straight instead of the outside straight,
                // as the straight would be higher and the odds are the same.
                bAceLow=true;
                break;
            }
        }
        else
        {
            // missing card.  Reset the "straight" counter.  We're looking for 4 in a row here, not inside straights.
            nHits=0;
        }
    }    

    if(nHits==4)
    {
        // we have 4 in a row, figure out which card to discard and replace with a new draw.
        for(int32 nJ=0;nJ<5;++nJ)
        {
            uint8 ucRank = mgr->getRank(getCardAt(nJ))-2;

            if(ucRank < (nTopRank-3) || ucRank > nTopRank)
            {
                // This is the card that's not part of the straight, replace it.
                uint8 ucNewCard = pShue->getCard();
                pNewCards->add(ucNewCard);
                getCards()[nJ] = ucNewCard;
                break;
            }
        }

        return;
    }
    else if(bAceLow)
    {
        // we have an ace low outside straight.  Figure out which card to replace.
        for(int32 nJ=0;nJ<5;++nJ)
        {
            uint8 ucRank = mgr->getRank(getCardAt(nJ));

            if((ucRank-2) > 2 && ucRank!=CardMgr::ACE)
            {
                // This is the card that's not part of the straight, replace it.
                uint8 ucNewCard = pShue->getCard();
                pNewCards->add(ucNewCard);
                getCards()[nJ] = ucNewCard;
                break;
            }
        }

        return;
    }

    // *****------ OKAY, no flush, no outside straight.  Look for an inside straight.
    nHits=0;
    int32 nHighRank=0, nLowRank=0;
    for(nI=12;nI>=0;--nI)   // go through the ranks looking for rank values we have in our hand.
    {
        if(aucCounts[nI]>0)
        {
            ++nHits;

            if(nHits==2)    // if we've already found two in a row, look to see if there's an inside straight around it.
            {
                if(nI!=0 && aucCounts[nI-1]>0 && nI<10 && aucCounts[nI+3]>0)
                {
                    nHighRank = nI+3;
                    nLowRank = nI-1;
                    break;
                }
                else if(nI>3 && aucCounts[nI-3]>0 &&
                        (aucCounts[nI-1]>0 || aucCounts[nI-2]>0))
                {
                    nHighRank = nI+1;
                    nLowRank = nI-3;
                    break;
                }
            }
        }
        else
        {
            // reset the "in a row" count to zero.  We start over.
            nHits=0;
        }
    }

    if(nHighRank != nLowRank)
    {
        // we found an inside straight draw!  Find which card to replace
        for(int32 nJ=0;nJ<5;++nJ)
        {
            uint8 ucRank = mgr->getRank(getCardAt(nJ)) - 2;

            if(ucRank < nLowRank || ucRank > nHighRank)
            {
                // This is the card that's not part of the straight, replace it.
                uint8 ucNewCard = pShue->getCard();
                pNewCards->add(ucNewCard);
                getCards()[nJ] = ucNewCard;
                break;
            }
        }        

        return;
    }


    // *****----- No flushes, no outside straights, no inside straights...just replace the lowest two cards!
    uint8 ucLow   = getNthHighest(3, HR_HIGHCARD);
    uint8 ucLower = getNthHighest(4, HR_HIGHCARD);

    for(int32 nJ=0;nJ<5;++nJ)
    {      
        uint8 ucCard = mgr->getRank(getCardAt(nJ));
        if( ucCard == ucLow || ucCard == ucLower)
        {          
            uint8 ucNewCard = pShue->getCard();
            pNewCards->add(ucNewCard);
            getCards()[nJ] = ucNewCard;
        }
    }        
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Called to figure out which cards to discard and replace in the dealer's hand, according to the cdraw
// rules, when the dealer already has two pair.  Here we'll just replace the single card that isn't
// part of a pair.
//
//  pShue     -  (in, out) The deck that we will deal any new replacement cards from.
//  pNewCards -  (out)     Put a copy of any drawn cards in here, we need to know about them later.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CDrawHand::processTwoPair(Shue *pShue, Hand *pNewCards)
{    
    uint8 ucLow = getNthHighest(4, HR_2PAIR);

    for(int32 nJ=0;nJ<5;++nJ)
    {      
        uint8 ucCard = mgr->getRank(getCardAt(nJ));
        if( ucCard == ucLow)
        {            
            uint8 ucNewCard = pShue->getCard();
            pNewCards->add(ucNewCard);
            getCards()[nJ] = ucNewCard; 
            break;
        }
    }        
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Called to figure out which cards to discard and replace in the dealer's hand, according to the cdraw
// rules, when the dealer already has 3 of a kind.  Here we'll replace the two cards that aren't part of 
// the three card set.
//
//  pShue     -  (in, out) The deck that we will deal any new replacement cards from.
//  pNewCards -  (out)     Put a copy of any drawn cards in here, we need to know about them later.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CDrawHand::processThreeOfAKind(Shue *pShue, Hand *pNewCards)
{
    uint8 ucLow   = getNthHighest(3, HR_3OFAKIND);
    uint8 ucLower = getNthHighest(4, HR_3OFAKIND);

    for(int32 nJ=0;nJ<5;++nJ)
    {      
        uint8 ucCard = mgr->getRank(getCardAt(nJ));
        if( ucCard == ucLow || ucCard == ucLower)
        {            
            uint8 ucNewCard = pShue->getCard();
            pNewCards->add(ucNewCard);
            getCards()[nJ] = ucNewCard;
        }
    }        
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Called to figure out which cards to discard and replace in the dealer's hand, according to the cdraw
// rules, when the dealer has a pair.  Here we'll just replace the single card that isn't part of the 
// pair.
//
//  pShue     -  (in, out) The deck that we will deal any new replacement cards from.
//  pNewCards -  (out)     Put a copy of any drawn cards in here, we need to know about them later.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CDrawHand::processPair(Shue *pShue, Hand *pNewCards)
{    
    uint8 ucLow   = getNthHighest(3, HR_PAIR);
    uint8 ucLower = getNthHighest(4, HR_PAIR);

    for(int32 nJ=0;nJ<5;++nJ)
    {      
        uint8 ucCard = mgr->getRank(getCardAt(nJ));
        if( ucCard == ucLow || ucCard == ucLower)
        {            
            uint8 ucNewCard = pShue->getCard();
            pNewCards->add(ucNewCard);
            getCards()[nJ] = ucNewCard;
        }
    }        
}

